/* commands.c

   commands supported by yaftp are
   handled here

   Miles Dung
*/

#define _GNU_SOURCE
#include <pthread.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <crypt.h>
#include <errno.h>
#include "server.h"
#include "lprintf.h"
#include "commands.h"

/* Read the parameter of the command from the client
 *
 * @param fd is the socket of the client
 * @param para is the parameter
 *
 * @return 0 successful
 *         -1 failed
 * */

#define DATA_FD_LOCK(mutex) {\
     if (pthread_mutex_lock((mutex)))\
     {\
	  lprintf("%s:%d lock mutex error.\n",\
		  __FUNCTION__, __LINE__);\
	  return NULL;\
     }\
}

#define DATA_FD_UNLOCK(mutex) {\
     if (pthread_mutex_unlock((mutex)))\
     {\
	  lprintf("%s:%d unlock mutex error.\n",\
		  __FUNCTION__, __LINE__);\
	  return NULL;\
     }\
}


void read_parameter(int fd, char *para, int n)
{
     char buf;
     int i = 0;
     
     //skip the white space
     while((recv(fd,&buf,sizeof(buf),0) > 0) && n - 1 > 0)
     {
	  if (buf == ' ' || buf == '\t' || buf == '\r')
	  {
	       continue;
	  }
	  else if (buf == '\n')
	  {
	       para[i] = 0;
	       return;
	  }
	  else
	  {
          para[i++] = buf;
          n--;
	  }
     }
	para[i] = 0;
	return;
}

/* examine the client_conn_>login_state
   0 init
   1 user name entered
   2 logined
*/
void cmd_user()
{
     char username[MAXLINE];

     if (client_conn->login_state == 2)
     {
	  static const char* msg =
	       "530 Already logged in\r\n";
	  send(client_conn->cmdfd, msg,
	       strlen(msg), 0);
	  return;
     }
     /* read username */
     read_parameter(client_conn->cmdfd,username,sizeof(username));


     if (client_conn->username == NULL)
     {
	  client_conn->username =
	       malloc(sizeof(char) * 256);
	  if (client_conn->username == NULL)
	  {
	       static const char* msg =
		    "451 Requested action aborted. "
		    "Local error in processing.\r\n";
	       lprintf("%s:%s Not enough memory\n", __FILE__,
		       __FUNCTION__);
	       send(client_conn->cmdfd, msg, strlen(msg), 0);
	       return;
	  }
     }
     strncpy(client_conn->username, username, sizeof(username));
     client_conn->login_state = 1;

     static const char* msg = "331 Username okay, "
	  "need password\r\n";
     send(client_conn->cmdfd, msg, strlen(msg), 0);

     return;
}

/* Functions shadow.h provide is not thread-safe
   use this fuction instead

   @return 0 if success, othres on error
*/
int user_auth(const char* username, const char* passwd)
{
     FILE* fp = fopen("/etc/shadow", "r");
     if (fp == NULL)
     {
	  lprintf("%s /etc/passwd could not open\n", __FUNCTION__);
	  return -1;
     }
     char buf[512];

     while(!feof(fp))
     {
	  fgets(buf, sizeof(buf), fp);
	  const char* i = buf;
	  const char* u = username;
	  while(*i != ':' && *u != '\0')
	  {
	       if (*i++ != *u++)
	       {
		    break;
	       }
	  }

	  if (*i == ':' && *u == '\0')
	  {
	       /* username match */
	       ++i; 		/* pass the ":" */
	       char* pass = malloc(128);
	       
	       for (int iter = 0; iter < 128; ++iter)
	       {
		    if (*i == ':')
		    {
			 pass[iter] = 0;
			 break;
		    }
		    pass[iter] = *i++;

	       }

	       /* we got the encrypt password, compare with user's input */
	       struct crypt_data* cr = malloc(sizeof(struct crypt_data));
	       cr->initialized = 0;
	       char* userpass = crypt_r(passwd, pass, cr);
	       
	       int tmp = strncmp(userpass, pass, 128);
	       free(pass);
	       free(cr);

	       return tmp;
	  }
     }
     
     return -1;
}

/* Read the password and compare it with */
void cmd_pass()
{
     char password[MAXLINE];


     /*read the password*/
     read_parameter(client_conn->cmdfd,password,sizeof(password));


     //check if username is provided
     if(client_conn->login_state == 0 ||
	client_conn->username == NULL)
     {
	  static char msg[] = "530 Send username first.\r\n";
	  send(client_conn->cmdfd, msg, sizeof(msg) - 1, 0);
	  return;
     }

     if(user_auth(client_conn->username, password))
     {
	  static char msg[] = "332 Username and password mismatch.\r\n";
	  send(client_conn->cmdfd, msg, sizeof(msg) - 1, 0);
     }
     else
     {
	  static char msg[] = "230 Login successfully.\r\n";
	  send(client_conn->cmdfd, msg, sizeof(msg) - 1, 0);
	  client_conn->login_state = 2;
	  lprintf("%s has logged in.\n",client_conn->username);

	  //TODO: switch user & chroot
     }

     return;
}


/* */
const char * cmd_list_ctime(const time_t modify_time, char *buf, size_t len)
{
     //get the current time
     time_t now_time;
     now_time = time(&now_time);

     struct tm modify_time_tm;
     localtime_r(&modify_time, &modify_time_tm);
     
     /* six month */
     if(time(NULL) - modify_time <= 3600*24*180)
     {
	  strftime(buf, len, "%b %d %H:%M", &modify_time_tm);
     }
     else
     {
	  strftime(buf, len, "%b %d %Y", &modify_time_tm);
     }

     return buf;
}

void cmd_list_ls(const char* name, struct stat* filest)
{
     char permission[11] = "----------";
     
     if (S_ISDIR(filest->st_mode))
     {
	  permission[0] = 'd';
     }

     /* user permission */
     if (S_IRUSR & filest->st_mode)
     {
	  permission[1] = 'r';
     }
     if(S_IWUSR & filest->st_mode)
     {
	  permission[2] = 'w';
     }
     if(S_IXUSR & filest->st_mode)
     {
	  permission[3] = 'x';
     }

     /* group permission */
     if(S_IRGRP & filest->st_mode)
     {
	  permission[4] = 'r';
     }
     if(S_IWGRP & filest->st_mode)
     {
	  permission[5] = 'w';
     }
     if(S_IXGRP & filest->st_mode)
     {
	  permission[6] = 'x';
     }

     /* others permission */
     if(S_IWOTH & filest->st_mode)
     {
	  permission[7] = 'r';
     }
     if(S_IWOTH & filest->st_mode)
     {
	  permission[8] = 'w';
     }
     if(S_IWOTH & filest->st_mode)
     {
	  permission[9] = 'x';
     }

     char buf[512];
     char timebuf[32] = {0};

     snprintf(buf, sizeof(buf),  "%s %5d %d %d %13d %s %s\r\n",
	      permission, filest->st_nlink, filest->st_uid,
	      filest->st_gid, filest->st_size,
	      cmd_list_ctime(filest->st_mtime, timebuf, sizeof(timebuf)),
	      name);
     send(client_conn->datafd, buf, strnlen(buf, sizeof(buf)), 0);
     return;
}

void* thread_list(void* arg)
{
     pthread_detach(pthread_self());
     
     DATA_FD_LOCK(client_conn->datafd_mutex);

     const char* path = arg;
     DIR* dir = opendir(path);
     struct stat stbuf;
     struct dirent ent;
     struct dirent* result = NULL;

     if (dir == NULL)
     {
	  /* ok, it's not a dir */
	  if (stat(path, &stbuf) == 0)
	  {
	       int flag = -1;
	       int i = 0;
	       while (path[i])
	       {
		    if (path[i] == '\\') /* find last slash */
		    {
			 flag = i;
		    }
		    i++;
	       }

	       /* TODO:  */
	       if (flag == -1)
	       {
		    cmd_list_ls(path, &stbuf);
	       }
	       else
	       {
		    cmd_list_ls(path + flag + 1, &stbuf);
	       }

	  }
	  /* even not a file */
	  else
	  {
	       char *msg = "530 The parameter is wrong.\r\n";
	       send(client_conn->cmdfd, msg, sizeof(msg) - 1, 0);
	  }
     }
     /* a dir, read it */
     else
     {
	  while(!readdir_r(dir, &ent, &result))
	  {
	       if (result == NULL)

	       {
		    break;
	       }
	       
	       char filename[256];
	       strcpy(filename, ent.d_name);

	       if(stat(filename, &stbuf) == 0)
	       {
		    cmd_list_ls(filename, &stbuf);

	       }
	  }
     }
     free(arg);
     
     close(client_conn->datafd);
     client_conn->datafd = 0;
     closedir(dir);
     
     {
	  static char msg[] = "226 Directory send OK.\r\n";
	  send(client_conn->cmdfd, msg, sizeof(msg) - 1, 0);
     }

     DATA_FD_UNLOCK(client_conn->datafd_mutex);

     return NULL;
}
/*handle the LIST command*/
void cmd_list()
{
     char* path = malloc(sizeof(char) * 256);
     int flag = 0;
     char buf;

     //memset(buf, 0, sizeof(buf));
     int state = 0;
     
     while(recv(client_conn->cmdfd, &buf,
		sizeof(buf), 0) > 0 &&
	   flag < sizeof(path) - 1)
     {
	  if (buf == '\r' || buf == ' ' || buf == '\t')
	  {
	       continue;
	  }
	  else if (buf == '\n')
	  {
           path[flag] = 0;
	       break;
	  }
      else if(buf == '-')
      {
           while(recv(client_conn->cmdfd, &buf,1,0) > 0)
           {
               if(buf == ' ' || buf == '\r')
                   break;
           }
      }
      else
      {
          path[flag++] = buf;
      }
     }
    
     /* we got the path, check if it contains parameter*/
     if (path[0] == '\0')
     {
	  path[0] = '.';
	  path[1] = 0;
     }

     char msg[]  = "150 Here comes the directory listing.\r\n";
     write(client_conn->cmdfd, msg, sizeof(msg) - 1);

     pthread_t tid;
     pthread_create(&tid, NULL, thread_list, path);
     
     return;
}

/*
 * get the filename list
 *
 * Command: NLST
 * */

void cmd_nlst()
{
     char dir[MAXLINE];
     struct dirent *ent = malloc(sizeof(struct dirent));
     struct stat *stbuf = malloc(sizeof(struct stat));

     /*read the full path */
     read_parameter(client_conn->cmdfd,dir,sizeof(dir));


     if(strlen(dir) == 0)//if the path is null
     {
	  dir[0] = '.';   //Set the default path is .;
	  dir[1] = '\0';
     }

     /*This if-then judge if the dir is a directory*/
     if(stat(dir,stbuf) == 0 && S_ISDIR(stbuf->st_mode))
     {
	  ;   //if it's a directory,do nothing
     }
     else
     {
	  static char *msg = "501 It is not a directory";
	  send(client_conn->cmdfd,msg, strlen(msg), 0);
	  return;
     }
    
     DIR *dp = opendir(dir);
     if(dp == NULL)
     {
	  lprintf("%s: can't open for dir", dir);
	  return;
     }

     /*read the directory*/
     while((ent = readdir(dp)) != NULL)
     {
	  send(client_conn->cmdfd, ent->d_name, strlen(ent->d_name), 0);
	  send(client_conn->cmdfd, "\r\n", 2, 0);
     }

     {
	  static char *msg = "230 Directory send OK\r\n";
	  send(client_conn->cmdfd,msg, strlen(msg), 0);
     }

     free(ent);
     free(stbuf);
     closedir(dp);

     return;
}

/*
 * create a directory
 *
 * Command: MKD
 * */

void cmd_mkd()
{
     char path[MAXLINE];
     int status;

     /* Read the path */
     read_parameter(client_conn->cmdfd,path,sizeof(path));



     status = mkdir(path,0777);
     if(status == 0)
     {
	  char *msg = "553 Can't create directory\r\n";
	  if(send(client_conn->cmdfd,msg, strlen(msg), 0) < 0)
	  {
	       lprintf("send msg error!\n");
	  }
	  return;
     }
     else
     {
	  lprintf("%s create a directory: \n", client_conn->username, path);
	  char *msg = "200 Create directory succesful\r\n";
	  send(client_conn->cmdfd, msg, strlen(msg), 0);
     }
}

/*
 * remove a directory
 *
 * Command: RMD
 * */

void cmd_rmd()
{
     char path[MAXLINE];

     /* read the path*/
     read_parameter(client_conn->cmdfd,path,sizeof(path));


     /* delete the path */
     if(rmdir(path) == 0)
     {
	  char *msg = "200 Remove a directory\r\n";
	  if(send(client_conn->cmdfd,msg, strlen(msg), 0) < 0)
	  {
	       lprintf("%s send error\n",__FUNCTION__);
	       return;
	  }
     }
     else
     {
	  char *msg = "503 Can't remove the Directory\r\n";
	  if(send(client_conn->cmdfd,msg, strlen(msg), 0) < 0)
	  {
	       lprintf("%s send error\n",__FUNCTION__);
	       return;
	  }
     }
}


/*
 * get the cunnent directory
 *
 * Command: PWD
 * */


void cmd_pwd()
{
     char buf[MAXLINE];
     char *p = buf;
    
     strncpy(p, "257 \"", 5);
     p = getcwd(buf + 5, sizeof(buf) - 5);
     strcat(p, "\"\r\n");
    
     if(send(client_conn->cmdfd, buf, strnlen(buf, sizeof(buf)), 0) < 0)
     {
	  lprintf("%s: send error\n", __FUNCTION__);
	  return;
     }
}

void*  pasv_thread(void* arg)
{
     pthread_mutex_t *mutex = (pthread_mutex_t*) client_conn->datafd_mutex;// arg;
     if (pthread_mutex_lock(mutex))
     {
	  lprintf("%s:%d lock mutex in thread error.\n",
		  __FUNCTION__, __LINE__);
     }
     lprintf("pasv thread entering accpet.\n");

     int newfd = accept(client_conn->datafd, NULL, NULL);
     if (newfd == -1)
     {
	  lprintf("%s:%d accept datafd error\n",
		  __FUNCTION__, __LINE__);
	  close(client_conn->datafd);
	  client_conn->datafd = 0;
	  return NULL;
     }
     
     close(client_conn->datafd);
     client_conn->datafd = newfd;

     lprintf("pasv thread exit.\n");

     if (pthread_mutex_unlock(mutex))
     {
	  lprintf("%s:%d unlock mutex in thread error.\n",
		  __FUNCTION__, __LINE__);
     }
	  
     return NULL;
}

/*
 * 
 *
 * Command: PASV
 * */

void cmd_pasv()
{
     srand(time(NULL));
     int datafd = 0;
     int rport = 0;
     lprintf("getting datafd");
     do {
	  if (datafd == -1)
	  {
	       sleep(1);
	  }
	  rport = server_conf->pasv_min_port + rand() % 
	       (server_conf->pasv_max_port - server_conf->pasv_min_port);
     
     } while ((datafd = get_listen_socket(rport)) == -1);

     lprintf("pasv datafd got\n");
     
     /* polling until a port is valid */
     /* this is ugly, we may find another way*/
     

     client_conn->datafd = datafd;
     
     struct sockaddr_in server_add;
     socklen_t len = sizeof(server_add);
     memset(&server_add, 0, sizeof(server_add));
     getsockname(client_conn->cmdfd, 
		 (struct sockaddr*) &server_add, 
		 &len);
     unsigned long localip = ntohl(server_add.sin_addr.s_addr);
     
     char buf[64];

     lprintf("prepering pass string\n");
     snprintf(buf, sizeof(buf), "227 Entering Passive Mode "
	      "(%d,%d,%d,%d,%d,%d).\r\n", (int)(localip >> 24) & 0xFF, 
	      (int)(localip >> 16) & 0xFF, (int)(localip >> 8) & 0xFF, 
	      (int)(localip) & 0xFF, (int)(rport >> 8) & 0xFF,
	      (int)(rport) & 0xFF);
	      
     lprintf(buf);

     /* create a thread to accept client on data socket */
     pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
     pthread_t tid;
/*
     if(pthread_mutex_lock(&mutex))
     {
	  lprintf("%s:%s mutex lock error\n", __FUNCTION__, __LINE__);

	  exit(1);
     }*/
     if(pthread_create(&tid, NULL, pasv_thread, &mutex))
     {
	  lprintf("%s:%d thread create failed!\n",
		  __FUNCTION__, __LINE__);
     }
     lprintf("pasv_thread created, tid: %ul\n", tid);

/*     if (pthread_mutex_lock(&mutex))
     {
	  lprintf("%s %s lock the mutex again error\n", __FUNCTION__, __LINE__);

	  pthread_mutex_destroy(&mutex);
	  exit(1);
     }
  */   
     //sleep(2);
     send(client_conn->cmdfd, buf, strnlen(buf, sizeof(buf)), 0);
     
     pthread_mutex_destroy(&mutex);
     return;
}


/*
 * Change the directory
 *
 * Command: CWD
 * */


void cmd_cwd()
{
     char path[MAXLINE];

     //Read the directory from the client
     read_parameter(client_conn->cmdfd,path,sizeof(path));


     /*Change the directory to "dir"*/
     if(chdir(path) < 0)
     {
	  char *msg = "550 Failed to change directory\r\n";
	  //send the error message to the client
	  send(client_conn->cmdfd, msg, strlen(msg), 0);
	  return;
     }
     else
     {
	  char *msg = "250 Directory successfuly changed\r\n";
	  send(client_conn->cmdfd, msg, strlen(msg), 0);
     }

     return;
}



/*
 * go to the parent directory
 *
 * Command: CDUP
 * */

void cmd_cdup()
{
     //The default parent directory is "."
     if(chdir("..") < 0)
     {
	  char *msg = "550 Failed to change directory\r\n";
	  //send the error message to the client
	  send(client_conn->cmdfd, msg, strlen(msg), 0);
	  return;
     }
     else
     {
	  char *msg = "250 Directory successfuly changed\r\n";
	  send(client_conn->cmdfd, msg, strlen(msg), 0);
     }

     return;

}


/*
 * Delete a file
 *
 *  Command: DELE
 * */

void cmd_dele()
{
     char path[MAXLINE];

     /*delete the file */
     read_parameter(client_conn->cmdfd,path,sizeof(path));


     if(remove(path) < 0)
     {
	  char *msg = "550 Can.t delete file\r\n";
	  send(client_conn->cmdfd, msg, strlen(msg), 0);
	  return;
     }
     else
     {
	  char *msg = "250 Delete the file\r\n";
	  send(client_conn->cmdfd, msg, strlen(msg), 0);
	  lprintf("%s delete %s\n",client_conn->username, path);
     }
}



/*
 * Rename the file, get the old name
 *
 * Command: RNFR
 *
 * */

void cmd_rnfr()
{
     char name[MAXLINE];

     /*read the old name*/
     read_parameter(client_conn->cmdfd,name,sizeof(name));

     //allocate memory for the oldname
     if((client_conn->oldname = malloc(strlen(name) + 1)) == NULL)
     {
	  char *msg = "530 System Error\r\n";
	  send(client_conn->cmdfd,msg,strlen(msg),0);
	  return;
     }
     strcpy(client_conn->oldname,name);

     static char msg[]  = "350 RNFR OK, send the new file name.\r\n";
     send(client_conn->cmdfd, msg, sizeof(msg) - 1, 0);
}


/*
 * Rename the file, change the old name to the new name
 *
 * Command: RNTO
 *
 * */

void cmd_rnto()
{
     char newname[MAXLINE];


     /*read the new name*/
     read_parameter(client_conn->cmdfd,newname,sizeof(newname));

     if(rename(client_conn->oldname,newname) < 0)
     {
	  char *msg = "530 Failed rename the file\r\n";
	  send(client_conn->cmdfd, msg, strlen(msg), 0);
	  return;
     }
     else
     {
	  char *msg = "250 Successful rename the file\r\n";
	  send(client_conn->cmdfd, msg, strlen(msg), 0);
     }

     free(client_conn->oldname);
}

/* Return the modification time of the file
 *
 * Command: MDTW
 *
 * */
void cmd_mdtw()
{
     time_t time;
     char filename[MAXLINE];
     struct stat file_stat;
     struct tm *mod_time = malloc(sizeof(struct tm));
     char buf[26];
     /* read the filename */
     read_parameter(client_conn->cmdfd,filename,sizeof(filename));

     if(stat(filename,&file_stat) < 0)
     {
	  char *msg = "530 System error\r\n";
	  send(client_conn->cmdfd, msg, strlen(msg),0);
	  return;
     }

     time = file_stat.st_mtime;
     mod_time = gmtime(&time);

     if(strftime(buf, 26, "%Y %m %d %X", mod_time) <= 0)
     {
	  char *msg = "530 System error\r\n";
	  send(client_conn->cmdfd, msg, strlen(msg),0);
	  return;
     }

     //Send the time to the client.
     strcat(buf,"\r\n");
     send(client_conn->cmdfd, buf, strlen(buf),0);

}

void* retr_thread(void* arg)
{
     char* filename = (char*) arg;

     DATA_FD_LOCK(client_conn->datafd_mutex);
     
     FILE* fp = fopen(filename, "r");
     if (fp == NULL)
     {
	  lprintf("%s: fp should not be NULL\n", __FUNCTION__);
	  free(filename);
	  DATA_FD_UNLOCK(client_conn->datafd_mutex);
	  return NULL;
     }

     /* this may have competition??? */

     {
	  static char msg[] = "150 Opening data connection.\r\n";
	  send(client_conn->cmdfd, msg, sizeof(msg) - 1, 0);
     }

     if (client_conn->datafd == -1)
     {
	  static char msg[] = "425 No data connection has been established.\r\n";
	  send(client_conn->cmdfd, msg, sizeof(msg) - 1, 0);

	  lprintf("%s accept error\n", __FUNCTION__);
	  fclose(fp);
	  free(filename);
	  DATA_FD_UNLOCK(client_conn->datafd_mutex);
	  return NULL;
     }     
     
     char buf[1024];
     while(!feof(fp))
     {
	  size_t n = fread(buf, sizeof(*buf), sizeof(buf), fp);
	  send(client_conn->datafd, buf, n, 0);
     }
     static char msg[] = "226 File has been sent successfully.\r\n";
     send(client_conn->cmdfd, msg, sizeof(msg) - 1, 0);
     
     /* do clean */
     free(filename);
     close(client_conn->datafd);
     client_conn->datafd = -1;
     DATA_FD_UNLOCK(client_conn->datafd_mutex);

     pthread_exit(NULL);
     
     return NULL;
}

/* retrive a remote file */
void cmd_retr()
{
     char* filename = malloc(sizeof(char)*256);
     
     read_parameter(client_conn->cmdfd, filename,256);


     /* check if file exist */
     FILE* fp = fopen(filename, "r");
     if (fp == NULL)
     {
	  static char msg[] = "451 Request file does not exist.\r\n";
	  send(client_conn->cmdfd, msg, sizeof(msg) - 1, 0);
	  return;
     }

     /* file exist */
     fclose(fp);
     pthread_t tid;
     tid = pthread_create(&tid, NULL, retr_thread, filename);

     /* TODO: detach will causing SEGSEV */
     // pthread_detach(tid);
     
     return;
}

void* thread_stor(void* arg)
{
     
     char* filename = (char*) arg;

     
     FILE* fp = fopen(filename, "w");
     if (fp == NULL)
     {
	  lprintf("%s: fp should not be NULL\n", __FUNCTION__);
	  free(filename);
	  static char msg[] = "553 Permission denied.\r\n";
	  send(client_conn->cmdfd, msg, sizeof(msg) - 1, 0);
	  return NULL;
     }

     DATA_FD_LOCK(client_conn->datafd_mutex);

     /* this may have competition??? */

     {
	  static char msg[] = "150 Opening data connection.\r\n";
	  send(client_conn->cmdfd, msg, sizeof(msg) - 1, 0);
     }

     if (client_conn->datafd == -1)
     {
	  static char msg[] = "425 No data connection has been established.\r\n";
	  send(client_conn->cmdfd, msg, sizeof(msg) - 1, 0);

	  lprintf("%s accept error\n", __FUNCTION__);
	  fclose(fp);
	  free(filename);

	  DATA_FD_UNLOCK(client_conn->datafd_mutex);
	  return NULL;
     }

     char recvbuf[1024];

     int ret = 0;
     while((ret = recv(client_conn->datafd, recvbuf, sizeof(recvbuf), 0)) > 0)
     {
	  fwrite(recvbuf, sizeof(char), ret, fp);
     }

     /* client sent FIN, file recv end */
     if (ret == 0)
     {
	  //TODO
	  static char msg[] = "226 File transfer end successfully.\r\n";
	  send(client_conn->cmdfd, msg, sizeof(msg) - 1, 0);	  
     }
     else
     {
	  /* some error happend */
	  lprintf("%s -1 return when recv\n", __FUNCTION__);
	  /* TODO: RETURN CODE */
	  static char msg[] = "553 Error happend, file transfer may not "
	       "completed.\r\n";
	  send(client_conn->cmdfd, msg, sizeof(msg) - 1, 0);
     }

     /* clean the mess */
     DATA_FD_UNLOCK(client_conn->datafd_mutex);
     fclose(fp);
     free(filename);
     
     return NULL;
}

void cmd_stor()
{
     char* filename = malloc(sizeof(char) * 256);
     read_parameter(client_conn->cmdfd, filename, 256);

     /* file can't write */
     /*
     if (access(filename, W_OK))
     {
	  lprintf("%s:%d file: %s cannot write.\n",
		  __FUNCTION__, __LINE__,  filename);
	  lprintf("%s\n", strerror(errno));
	  static char msg[] = "553 Permission denied, file cannot write.\r\n";
	  send(client_conn->cmdfd, msg, sizeof(msg) - 1, 0);
	  return;
     }
     */
     /* file ok, receive file from data socket */
     pthread_t tid;
     pthread_create(&tid, NULL, thread_stor, filename);
     pthread_detach(tid);

     return;
}

void cmd_syst()
{
     static char msg[] = "215 UNIX Type: L8\r\n";
     send(client_conn->cmdfd, msg, sizeof(msg) - 1, 0);

     return;
}

/* UNKNOWN command goes here */
void cmd_unknown()
{
     char buf;
     int ret = 0;
     int state = 0;
     /* read a whole line */
     char clearbuf[512];
     recv(client_conn->cmdfd, clearbuf, sizeof(clearbuf), 0);
     /*
       while(1)
       {
       if ((ret = recv(client_conn->cmdfd, &buf, sizeof(buf), 0)) == 1)
       {
       if (buf == '\n' && state == 1)
       {
       break;
       }
       else if (buf == '\r')
       {
       state = 1;
       continue;
       }
       else
       {
       state = 0;
       }
       }
       else if (ret == 0)
       {
       lprintf("%s client closed cmd connetion.\n", __FUNCTION__);
       close(client_conn->cmdfd);
       exit(0);
       }
       else if (ret < 0)
       {
       lprintf("%s recv: %d\n", __FUNCTION__, ret);
       return;
       }
       else
       {
       lprintf("%s %s should not reach here\n");
       }

       }
     */
     static char msg[] = "500 Unknown command.\r\n";
     send(client_conn->cmdfd, msg, sizeof(msg) - 1, 0);

     return;
}

/* Return the size of the file
 *
 * Command: SIZE
 *
 * */

void cmd_size()
{
     char filename[MAXLINE];
     struct stat file_stat;
     char buf[256];   //store the size of the file

     //read the file name
     read_parameter(client_conn->cmdfd,filename,sizeof(filename));

     if(stat(filename, &file_stat) < 0)
     {
	  char *msg = "530 System error\r\n";
	  send(client_conn->cmdfd, msg, strlen(msg),0);
	  return;
     }

     sprintf(buf, "213 The size of %s is %d.\r\n", filename, file_stat.st_size);
     send(client_conn->cmdfd,buf,strlen(buf), 0);
}


void cmd_noop()
{
     static char msg[] = "200 Command OK.\r\n";
     send(client_conn->cmdfd, msg, sizeof(msg) - 1, 0);

     return;
}

void cmd_type()
{
     char type[4];

     read_parameter(client_conn->cmdfd, type, sizeof(type));
     lprintf("client set type to %s, ignore in current version.\n",
	     type);
     
     static char msg[] = "200 TYPE mode set successfully.\r\n";
     send(client_conn->cmdfd, msg, sizeof(msg) - 1, 0);
     
     return;
}

void cmd_quit()
{
     static char msg[] = "221 Good bye~\r\n";
     send(client_conn->cmdfd, msg, sizeof(msg) - 1, 0);

     close(client_conn->cmdfd);
     close(client_conn->datafd);
     free(client_conn);

     lprintf("client request quit, process exit.\n");

     exit(0);
     
     return;
}

void cmd_parser()
{
     while (1)
     {
	  char cmdbuf[8];
	  int

	       flag = 0, n = 0;
	  char buf;

  
	  //skip the white space
	  while(recv(client_conn->cmdfd,
		     &buf, sizeof(buf), 0) > 0)
	  {
	       if (buf == ' ' || buf == '\t' ||
		   buf == '\r' || buf == '\n')
	       {
		    continue;
	       }
	       else
	       {
		    break;
	       }
	  }
	  cmdbuf[flag++] = buf & 0xDF; //to capital
   
	  alarm(client_conn->client_timeout);
	  /* read command from socket */
	  while(flag < sizeof(cmdbuf) - 1 &&
		(n = recv(client_conn->cmdfd,
			  &buf, sizeof(buf), 0)) > 0)
	  {
	       if (buf == ' ' || buf == '\r')

	       {
		    break;
	       }
	       /* convert to capital */
	       cmdbuf[flag++] = buf & 0xDF; /* 1101, 1111 */
	  }
	  if (n <= 0)
	  {
	       lprintf("client cmd connection closed.\n");
	       exit(0);
	       return;
	  }
	  cmdbuf[flag] = '\0';

	  lprintf("cmd: %s\n", cmdbuf);

	  int ret = strncmp(cmdbuf, "USER", sizeof(cmdbuf));
	  if (ret == 0)
	  {
	       cmd_user();
	  }
	  else if (strncmp(cmdbuf, "PASS", sizeof(cmdbuf)) == 0)
	  {
	       cmd_pass();
	  }
	  else if (strncmp(cmdbuf, "PASV", sizeof(cmdbuf)) == 0)
	  {
	       cmd_pasv();
	  }
	  else if(strncmp(cmdbuf,"LIST",sizeof(cmdbuf)) == 0)
	  {
	       cmd_list();
	  }
	  else if(strncmp(cmdbuf,"NLST",sizeof(cmdbuf)) == 0)
	  {
	       cmd_nlst();
	  }
	  else if(strncmp(cmdbuf,"MKD",sizeof(cmdbuf)) == 0)
	  {
	       cmd_mkd();
	  }
	  else if(strncmp(cmdbuf,"RMD",sizeof(cmdbuf)) == 0)
	  {
	       cmd_rmd();
	  }
	  else if(strncmp(cmdbuf,"PWD",sizeof(cmdbuf)) == 0)
	  {
	       cmd_pwd();
	  }
	  else if(strncmp(cmdbuf,"CWD",sizeof(cmdbuf)) == 0)
	  {
	       cmd_cwd();
	  }
	  else if(strncmp(cmdbuf,"CDUP",sizeof(cmdbuf)) == 0)
	  {
	       cmd_cdup();
	  }
	  else if(strncmp(cmdbuf,"DELE",sizeof(cmdbuf)) == 0)
	  {
	       cmd_dele();
	  }
	  else if(strncmp(cmdbuf,"RNFR",sizeof(cmdbuf)) == 0)
	  {
	       cmd_rnfr();
	  }        
	  else if(strncmp(cmdbuf,"RNTO",sizeof(cmdbuf)) == 0)
	  {
	       cmd_rnto();
	  }
	  else if(strncmp(cmdbuf, "RETR", sizeof(cmdbuf)) == 0)
	  {
	       cmd_retr();
	  }
	  else if(strncmp(cmdbuf, "STOR", sizeof(cmdbuf)) == 0)
	  {
	       cmd_stor();
	  }
	  else if(strncmp(cmdbuf,"MDTW",sizeof(cmdbuf)) == 0)
	  {
	       cmd_mdtw();
	  }
	  else if(strncmp(cmdbuf,"SIZE",sizeof(cmdbuf)) == 0)
	  {
	       cmd_size();
	  }
	  else if (strncmp(cmdbuf,"SYST",sizeof(cmdbuf)) == 0)
	  {
	       cmd_syst();
	  }
	  else if (strncmp(cmdbuf,"QUIT",sizeof(cmdbuf)) == 0)
	  {
	       cmd_quit();
	  }
	  else if (strncmp(cmdbuf,"TYPE",sizeof(cmdbuf)) == 0)
	  {
	       cmd_type();
	  }
	  else if (strncmp(cmdbuf,"NOOP",sizeof(cmdbuf)) == 0)
	  {
	       cmd_noop();
	  }
	  	  	  
	  else
	  {
	       lprintf("Unknown command: %s\n", cmdbuf);
	       cmd_unknown();
	  }
     }
     return;
}

